home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
001
/
comzap.arc
/
COMZAP.ASM
next >
Wrap
Assembly Source File
|
1985-07-08
|
11KB
|
343 lines
page 60,132
title COMZAP.ASM
;...................
;Author: Edward Nisley
;Entered from July 1985 issue of PC Tech Journal
;...................
;Program to force DOS 2.0 and 2.1 to reload COMMAND.COM
; using the file name specified by SET COMSPEC=
;Particularly useful with Vdisks and non-bootable
; fixed disks.
;
;The DOS 2.0 and 2.1 SET COMSPEC= command does not work as
; documented. DOS will always attempt to reload COMMAND.COM
; from the root directory of the disk drive used during
; system initialization (the boot disk).
;
;This utility finds the string A:\COMMAND.COM in DOS
; and replaces it with the file name set by SET COMSPEC=
;
;How it's done:
; the BIOS memory-size interrupt determines the
; range of RAM searched for the string.
;The string A:\COMMAND.COM always starts at an
; offset address xxx9, with a binary zero byte at
; offset xxx8 just preceding it. These observations
; are used to distinguish the true DOS file name from
; the string elsewhere (as in the program.COM file
; a VDISK or the program as loaded in RAM)
;[Or from same in the keyboard buffer under DEBUG.]
;Offset 2C in the PSP contains the segment address of
; the environment area passed to this program. The
; COMSPEC= string is located within the envrionment.
;The COMSPEC= string is copied to the DOS string area
; and displayed on the screen as it is being copied.
;If the COMSPEC= string is too long, your PC is dead!
;The string is truncated to fit the largest space
; available, which means that it cannot be used to
; reload the command interprteer. Power-off time!
;
;Assumptions:
;-DOS 2.0 or DOS 2.1
;-Power-on boot drive is "A"
;-You have enough RAM in your system that the
; string is not in the last segment. The last segment
; is not checked, except in 64K systems.
;-The SET COMSPEC string is not too long. The maximum
; length is one less than the number defined in cspecmax.
;
;Exit: DOS function 4CH is used to pass a return code
; code 0=success!
; code 1=DOS file name string wasn't found
; code 2=COMSPEC string wasn't found
; code 3=COMSPEC string was too long, truncated
;
page
;...........................
;hocus pocus to start up the assembler
; and get the addresses correct for a .COM file
comseg segment 'codeseg'
assume cs:comseg,ds:comseg,ss:comseg,es:comseg
comzap proc
org 100H
start: jmp around
;.........................
;The file name we are trying to find. The leading
; 0FFH byte ensures that this file name isn't preceded
; by a binary zero. This prevents incorrect matching
; when Murphy puts it at the correct offset.
db 0FFH
bootID db 'A' ;the normal boot disk
CCstr db ':\COMMAND.COM' ;last part of string
CCstr_l equ $-CCstr ;string length
;.........................
;The COMSPEC= string header in the environment area
specstr db 'COMSPEC='
specstr_l equ $-specstr ;string length
;.........................
;Various & sundry constants
FN_hexit dw 0009H ;file name offset hexit
env_seg_at equ 002CH ;address of environment segment
prt_chr equ 02H ;DOS single-character output
prt_str equ 09H ;DOS print-string function
quit equ 4CH ;DOS termination function
mem_sz equ 12H ;BIOS memory-size interrupt
dosint equ 21H ;DOS function interrupt
cr equ 0DH ;useful characters
lf equ 0Ah
term equ '$' ;the prt_str terminator
;.....................
;A macro to simplify the DOS interface
DOScall macro DOS_fn
mov ah,DOS_fn
int dosint
endm
page
;.......................
;Variables
FN_at label dword ;seg:offset address of
FN_off dw 0 ;DOS file name string
FN_seg dw 0
cspec_at label dword ;seg:offset of COMSPEC=
cspec_off dw 0 ;string in enfironment
cspec_seg dw 0
cspecmax equ 39 ;longest string allowed
; includes trailing zero
last_seg dw 0 ;last segment to check
;.......................
;Messages
Msg0 db 'COMZAP: a DOS fixit utility',cr,lf
db 'Revised 4 April 85',cr,lf
db cr,lf
db 'Searching for A:\COMMAND.COM...'
CrLf db cr,lf,term
Msg1 db ' >> not found.',cr,lf
db ' >> Remember that COMZAP.COM can'
db cr,lf
db ' >> be run only once per cold boot'
db cr,lf,term
Msg2 db cr,lf
db 'The DOS command interpreter will be'
db 'reloaded from',cr,lf
db ' ',term
Msg3 db 'Searching for COMSPEC= string...'
db cr,lf,term
Msg4 db ' >> not found.',cr,lf
db ' >> Did you use SET COMSPEC=filespec'
db cr,lf
db ' >> before running COMZAP.COM?'
db cr,lf,term
Msg5 db cr,lf
db ' >> WARNING: COMSPEC string too long,'
db cr,lf
db ' >> DOS will not reload the interpreter'
db cr,lf,term
page
;........................
;Start of the program code
;
;Show some identification
around: cld ;set increment mode
mov dx,offset Msg0 ;show ID message
DOScall prt_str
;.........................
;Get storage size and convert to segment address.
;Because the last segment may not be full, we will not
; test it for the string.
;Because we are testing in units of 64K (full segments)
; only the high-order hexit of the segment address
; is useful in comparisons.
;64K PC's (are there any left?) are special-cases.
int mem_sz ;storage in 1K units
mov cl,6 ;shift MSB to bit 15
shl ax,cl
and ax,0F000H ;isolate 64K seg hexit
jz oneseg ;handle 64K machines
sub ax,01000H ;set last segment
oneseg: mov last_seg,ax
;........................
;Search for the starting "A" in "A:\COMMAND.COM"
;The inner loop uses CMP to check the 4096 offset
; addresses ending in the hexit 9 in each segment.
;The outer loop verifies inner-loop hits and ticks
; the segment addresses at the end of segments
mov al,bootID ;the test byte
mov bx,00000H ;first segment address
mov es,bx ;into segment register
assume es:nothing ;tell the assembler
CClp0: mov si,FN_hexit ;get the offset address
sub si,0010h ;fix for pre-increment
mov cx,1000H ;64K/16 = # of 9's
CClp1: add si,0010H ;tick offset pointer
cmp al,es:[si] ;test the byte
loopne CClp1 ;loop if not equal
;.....................
;Inner loop ended:
; if "not equal", the entire segment has been examined
; without finding a qualifying "A", so go to next seg
jne nextseg
;......................
;We've found an "A" at an offset xxx9
; if it's preceded by a binary zero, look closer...
cmp byte ptr es:[si-1],00H
je testmore ;hmm... looking good!
page
;.......................
;The "A" doesn't pass muster, so continue with the
; next byte in the segment.
;If CX is zero, all bytes have been tested,
; so we must step to the next segment
cmp cx,0
jne CClp1
jmp nextseg
;......................
;An "A" and a binary zero have been located!!!
;Time to compare the strings.
;Save the current state, just in case it's a miss
testmore:
push si ;save current offest
push es ; ... segment
push cx ; ... count
push ax ; ... test byte
;.................
;Set the segment address to the paragraph just before
; the string to avoid overrunning the segment boundary
;This is complicated a bit by the restricted nature of
; the 8088's "general" registers
mov bx,si ;get current offset
mov cl,4 ;convert to paragraphs
shr bx,cl
mov cx,es ;add the segment
add bx,cx
mov es,bx ;set in in seg reg
mov FN_seg,bx ;save segment
;.....................
;Set up the offset addresses to the known string and
; the candidate we just found
;The candidate offset is always FN_hexit by definition
mov di,FN_hexit ;known candidate offset
mov FN_off,di ;save offset
inc di ;step over "A"
mov si,offset CCstr ;known string
mov cx,CCstr_l ; ... its length
;....................
;After all that, this is the string com0parison.
;Registers are restored before the branch simply to
; avoid a double branch.
;The CMPS operands are coded for human readability.
;Failure returns us to the inner test loop above.
;Success passes us to the COMSPEC= string eaarch.
repe cmps byte ptr ds:[si],es:[di]
pop ax ;restore saved regs
pop cx
pop es
pop si
jne CClp1 ;no good, continue
jmp gotfile ;swell, break out
page
;.....................
;outer loop ending...
;Didn't find the string in this segment
;Trick the segment until we've completed the search.
;Remember that the RAM in the last segment will not
; be examined because the segment may not be filled
; with RAM...
;A tige more code would make the search complete.
nextseg:
mov bx,es ;tick the segment pointer
add bx,1000H
mov es,bx
cmp bx,last_seg ;done?
jbe CClp0 ;nope, back to loops
mov dx,offset Msg1 ;present error message
DOScall prt_str
mov al,1 ;return code
DOScall quit
page
;......................
;Tah-DAH! Found the DOS file specifier in RAM.
;Now we have to look for the COMSPEC string.
;Each string in the environment is terminated with a
; binary zero. The end of the environment area is
; marked by two binary zeros.
;Note how the "general" pointer registers aren't...
gotfile:
mov dx,offset Msg3 ;say where we are
DOScall prt_str
mov si,env_seg_at ;points to env seg ptr
mov es,[si] ;pick up segment addr
mov cspec_seg,es ;save seg addr
findspec:
mov di,cspec_off ;get offs addr
cmp byte ptr es:[di],00h ;at end?
je nospec ;sigh, give up
mov si,offset specstr ;test string
mov cx,specstr_l
repe cmps byte ptr ds:[si],es:[di]
je gotspec ;kapow!
mov si,di ;use right "general" reg
junkit: lods byte ptr es:[si] ;scan for end
cmp al,00H
jne junkit
mov cspec_off,si ;set up pointer
jmp findspec ;and try again
;..........................
;Didn't find a COMSPEC= string in the environment.
;Return to DOS with an error code.
nospec:
mov dx,offset Msg4 ;show a sign
DOScall prt_str
mov al,2 ;set return code
DOScall quit
page
;......................
;Found the COMSPEC= string in the enfironment area
;ES:DI now points to the start of the file spec string
;Need to get source in DS:SI and target in ES:DI
;Transfer the COMSPEC= string to the DOS file string
; and display it as it is transferred.
;The trailing binary zero is transferred as well, to
; overwrite any previous file name you may have
; manually inserted into the DOS file string while
; debugging this program...
;The string will be truncated to prevent stamping on
; other DOS data following the file name area.
gotspec:
mov cspec_off,di ;save offset
mov dx,offset Msg2 ;say what's happening
DOScall prt_str
mov cx,cspecmax ;max length allowed
les di,FN_at ;DOS string address
lds si,cspec_at ;COMSPEC string addr
xfer: lods byte ptr ds:[si] ;COMSPEC char
stos byte ptr es:[di] ;to DOS char
cmp al,00H ;hit the end?
je alldone ;yup, quit
mov dl,al ;nope, show it
DOScall prt_chr
loop xfer ;repeat for count
;if fall through, error!
mov ax,cs ;restore DS
mov ds,ax
mov dx,offset Msg5 ;tell them about truncation
DOScall prt_str
mov al,3 ;set return code
DOScall quit
;..................
;Done with transfer and display, return to DOS
alldone:
mov ax,cs ;restore DS
mov ds,ax
mov dx,offset crlf ;insert some space
DOScall prt_str
mov al,0 ;good return code
DOScall quit ;and exit
page
;....................
;hocus pocus to turn off the assembler
; and set up .COM starting address
comzap endp
comseg ends
end start